﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using DiscoveryLogic.Analytics.BusinessLogic;
using DiscoveryLogic.Analytics.BusinessLogic.Dedups;
using DiscoveryLogic.Analytics.BusinessLogic.Links;
using DiscoveryLogic.Analytics.BusinessLogic.Rules;
using DiscoveryLogic.Analytics.BusinessLogic.Searches;
using DiscoveryLogic.Analytics.DataObjects;
using DiscoveryLogic.Analytics.DataObjects.Dedup;
using DiscoveryLogic.Analytics.DataObjects.Networks;
using DiscoveryLogic.Analytics.DataObjects.Rules;
using DiscoveryLogic.Analytics.DataObjects.Visuals;
using DiscoveryLogic.Common.Utilities;

namespace DiscoveryLogic.Analytics.Modules.Searches
{
    /// <summary>
    /// Interaction logic for QuickSearch.xaml
    /// </summary>
    public partial class QuickSearch : UserControl
    {
        public delegate void OnNetworkCreated(Network network);
        public delegate void OnNetworkUpdated(Network network);
        public event OnNetworkCreated NetworkCreated;
        public event OnNetworkUpdated NetworkUpdated;

        private Window _OwnerWindow;
        private Popup _OwnerPopup;

        private Dictionary<string, EntityRelationRule> _Rules;

        public QuickSearch(Window ownerWindow)
        {
            InitializeComponent();
            this._OwnerWindow = ownerWindow;
        }

        public QuickSearch(Popup ownerPopup)
        {
            InitializeComponent();
            _OwnerPopup = ownerPopup;
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            this.cboEntity.Items.Clear();
            foreach(EntityType entityType in Enum.GetValues(typeof(EntityType)))
            {
                this.cboEntity.Items.Add(entityType.ToString());
            }

            this._Rules=new Dictionary<string, EntityRelationRule>();
            this.cboLinkRules.Items.Clear();
            List<RuleBase> rules = RuleManager.GetRulesByType(RuleType.Link, EntityIcons.LinkRuleIcon);
            
            if(this._Rules !=null && this._Rules.Count>0)
            {
                foreach (string ruleName in this._Rules.Keys)
                    this.cboLinkRules.Items.Add(ruleName);
            }
        }

        private void cboEntity_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if(this.cboEntity.SelectedIndex>=0)
            {
                EntityType entityType =
                    (EntityType) Enum.Parse(typeof (EntityType), this.cboEntity.SelectedItem.ToString());
                PropertyInfo[] props = EntityTypeUtil.GetProperties(entityType);
                
                this.cboField.Items.Clear();
                foreach(PropertyInfo prop in props)
                {
                    if (prop.CanWrite && prop.CanRead)
                        this.cboField.Items.Add(prop.Name);
                }
            }
        }

        private void btnSearch_Click(object sender, RoutedEventArgs e)
        {
            if(!string.IsNullOrEmpty(this.txtSearch.Text) && 
                this.cboEntity.SelectedIndex>=0 && 
                this.cboField.SelectedIndex>=0)
            {
                try
                {
                    EntityType entityType =
                        (EntityType) Enum.Parse(typeof (EntityType), this.cboEntity.SelectedItem.ToString());
                    string fieldName = this.cboField.SelectedItem.ToString();
                    List<EntityBase> entities = EntitySearchUtil.Search(entityType, fieldName, this.txtSearch.Text);
                    switch (entityType)
                    {
                        case EntityType.Grants:
                            List<GrantDO> grants = new List<GrantDO>();
                            foreach (EntityBase entity in entities)
                            {
                                grants.Add(entity as GrantDO);
                            }
                            this.dgSearchResult.ItemsSource = grants;
                            break;
                        case EntityType.Persons:
                            List<PersonDO> persons = new List<PersonDO>();
                            foreach (EntityBase entity in entities)
                            {
                                persons.Add(entity as PersonDO);
                            }
                            this.dgSearchResult.ItemsSource = persons;
                            break;
                        case EntityType.Organizations:
                            List<ExternalOrgDO> orgs = new List<ExternalOrgDO>();
                            foreach (EntityBase entity in entities)
                            {
                                orgs.Add(entity as ExternalOrgDO);
                            }
                            this.dgSearchResult.ItemsSource = orgs;
                            break;
                        case EntityType.Publications:
                            List<PublicationDO> pubs = new List<PublicationDO>();
                            foreach (EntityBase entity in entities)
                            {
                                pubs.Add(entity as PublicationDO);
                            }
                            this.dgSearchResult.ItemsSource = pubs;
                            break;
                    }
                }
                catch(Exception ex)
                {
                    System.Windows.Forms.MessageBox.Show(ex.Message);
                }
            }
        }

        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            if(_OwnerWindow !=null)
                _OwnerWindow.Close();
            else if(_OwnerPopup!=null)
                _OwnerPopup.IsOpen = false;
        }

        private void btnAddToCurrentGraph_Click(object sender, RoutedEventArgs e)
        {
            EntityType entityType =
                    (EntityType)Enum.Parse(typeof(EntityType), this.cboEntity.SelectedItem.ToString());
            List<EntityBase> entities = new List<EntityBase>();
            if (this.ckAddSelected.IsChecked.Value)
            {
                foreach (Object entity in this.dgSearchResult.SelectedItems)
                {
                    entities.Add(entity as EntityBase);
                }
                // entities = (List<EntityBase>)this.dgSearchResult.SelectedItems;
            }
            else
            {
                switch (entityType)
                {
                    case EntityType.Grants:
                        List<GrantDO> grants = (List<GrantDO>) this.dgSearchResult.ItemsSource;
                        foreach (GrantDO grant in grants)
                            entities.Add(grant);
                        break;
                    case EntityType.Organizations:
                        List<ExternalOrgDO> orgs = (List<ExternalOrgDO>) this.dgSearchResult.ItemsSource;
                        foreach (ExternalOrgDO org in orgs)
                            entities.Add(org);
                        break;
                    case EntityType.Persons:
                        List<PersonDO> persons = (List<PersonDO>) this.dgSearchResult.ItemsSource;
                        foreach (PersonDO person in persons)
                            entities.Add(person);
                        break;
                    case EntityType.Publications:
                        List<PublicationDO> pubs = (List<PublicationDO>) this.dgSearchResult.ItemsSource;
                        foreach (PublicationDO pub in pubs)
                            entities.Add(pub);
                        break;
                }
            }
            if (entities == null && entities.Count == 0)
            {
                MessageBox.Show("Empty search results");
            }

            // make sure PKValue is unique
            List<string> keys=new List<string>();
            foreach(EntityBase entity in entities)
            {
                if(entity.PKValue() !="0" && !keys.Contains(entity.PKValue()))
                {
                    keys.Add(entity.PKValue());
                }
                else
                {
                    PropertyInfo idProp = entity.GetType().GetProperty(entity.IDField);
                    SimpleDataType idDbType = SimpleDataTypeUtil.ToSimpleDataType(idProp.PropertyType);
                    int hashKey = entity.GetHashCode();
                    object idValue = SimpleDataTypeUtil.ToDbValue(idDbType, hashKey);
                    idProp.SetValue(entity, idValue, null);
                    keys.Add(idValue.ToString());
                }
            }
            
            if (this.rdNewNetwork.IsChecked.Value)
            {
                    Network newNetwork = new Network(this.txtNewNetwork.Text, entities, new List<Link>());
                    newNetwork.Save();
                    MyData.ReadAllNetworks();
                    if (this.NetworkCreated != null)
                        this.NetworkCreated(newNetwork);

                    if (_OwnerWindow != null)
                        _OwnerWindow.Close();
                    else if (_OwnerPopup != null)
                        _OwnerPopup.IsOpen = false;
            }
            else
            {
                if (this.cboExistingNetworks.SelectedIndex >= 0 &&
                    this.cboLinkRules.SelectedIndex >= 0)
                {
                    EntityRelationRule rule = this._Rules[this.cboLinkRules.Text];
                    if (rule.TgtEntityType != entityType)
                    {
                        throw new Exception("Rule target type does not match");
                    }

                    Network network = Network.Read(this.cboExistingNetworks.SelectedItem.ToString());
                    List<string> acceptedTgtEntityKeys = new List<string>();
                    foreach (EntityBase entity in network.Records)
                    {
                        if (entity.EntityType == entityType)
                            acceptedTgtEntityKeys.Add(entity.PKValue());
                    }
                    foreach (EntityBase entity in entities)
                    {
                        if (!acceptedTgtEntityKeys.Contains(entity.PKValue()))
                            acceptedTgtEntityKeys.Add(entity.PKValue());
                    }
                    List<string> srcKeys = new List<string>();
                    foreach (EntityBase entity in network.Records)
                    {
                        if (entity.EntityType == rule.SrcEntityType)
                            srcKeys.Add(entity.PKValue());
                    }
                    EntityRelationWorker worker = new EntityRelationWorker(rule, network, srcKeys);
                    network = worker.ReadRelatedData();
                    if (NetworkDedupSetting.Exists(network.NetworkName))
                    {
                        NetworkDedupSetting dedupSetting = NetworkDedupSetting.Read(network.NetworkName);
                        NetworkDedupWorker dedupWorker = new NetworkDedupWorker(network, dedupSetting);
                        network = dedupWorker.DedupNetwork();
                    }
                    int idx = 0;
                    while (idx < network.Records.Count)
                    {
                        EntityBase entity = network.Records[idx];
                        if (entity.EntityType == entityType && !acceptedTgtEntityKeys.Contains(entity.PKValue()))
                            network.Records.Remove(entity);
                        else
                            idx++;
                    }

                    Type fromType = null;
                    switch (rule.SrcEntityType)
                    {
                        case EntityType.Grants:
                            fromType = typeof (GrantDO);
                            break;
                        case EntityType.Organizations:
                            fromType = typeof (ExternalOrgDO);
                            break;
                        case EntityType.Persons:
                            fromType = typeof (PersonDO);
                            break;
                        case EntityType.Publications:
                            fromType = typeof (PublicationDO);
                            break;
                    }
                    Type toType = null;
                    switch (rule.TgtEntityType)
                    {
                        case EntityType.Grants:
                            toType = typeof (GrantDO);
                            break;
                        case EntityType.Organizations:
                            toType = typeof (ExternalOrgDO);
                            break;
                        case EntityType.Persons:
                            toType = typeof (PersonDO);
                            break;
                        case EntityType.Publications:
                            toType = typeof (PublicationDO);
                            break;
                    }
                    idx = 0;
                    if (network.Links != null && network.Links.Count > 0)
                    {
                        while (idx < network.Links.Count)
                        {
                            Link link = network.Links[idx];
                            if (link.FromType == fromType && link.ToType == toType)
                            {
                                if (!acceptedTgtEntityKeys.Contains(link.ToKey))
                                    network.Links.Remove(link);
                                else
                                    idx++;
                            }
                            else
                                idx++;
                        }
                    }

                    network.Save();
                    if (this.NetworkUpdated != null)
                        this.NetworkUpdated(network);

                    if (_OwnerWindow != null)
                        _OwnerWindow.Close();
                    else if (_OwnerPopup != null)
                        _OwnerPopup.IsOpen = false;
                }
            }
        }

        private void rdNewNetwork_Checked(object sender, RoutedEventArgs e)
        {
            if (this.IsInitialized)
            {
                if (rdNewNetwork.IsChecked.Value)
                {
                    this.txtNewNetwork.Visibility = Visibility.Visible;
                    this.cboExistingNetworks.Visibility = Visibility.Hidden;
                    this.rdCurrentNetwork.IsChecked = false;
                }
                else
                {
                    this.txtNewNetwork.Visibility = Visibility.Hidden;
                    this.cboExistingNetworks.Visibility = Visibility.Visible;
                    this.rdCurrentNetwork.IsChecked = true;
                }
            }
        }

        private void rdCurrentNetwork_Checked(object sender, RoutedEventArgs e)
        {
            if (this.IsInitialized)
            {
                if (this.rdCurrentNetwork.IsChecked.Value)
                {
                    this.txtNewNetwork.Visibility = Visibility.Hidden;
                    this.cboExistingNetworks.Visibility = Visibility.Visible;
                    this.rdNewNetwork.IsChecked = false;
                }
                else
                {
                    this.txtNewNetwork.Visibility = Visibility.Visible;
                    this.cboExistingNetworks.Visibility = Visibility.Hidden;
                    this.rdNewNetwork.IsChecked = true;
                }
            }
        }

    }
}
